package top.v5it.japi.plus.sdk;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.util.StringValueResolver;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;

/**
 * 解析并处理http请求
 *
 * @author zhanpu
 * @date 2022/6/23
 */
@Slf4j
@Aspect
public class JapiApiAspect implements EmbeddedValueResolverAware {

    /**
     * 用时超时时间
     */
    private static final long TIMEOUT = 3000L;

    private final Map<cn.hutool.http.Method, BiFunction<JapiApi, Object[], Object>> mapper;
    private StringValueResolver resolver;

    /**
     * 构造函数
     *
     * @param stringValueResolver {@link StringValueResolver}
     */
    public JapiApiAspect() {
        this.mapper = new HashMap<cn.hutool.http.Method, BiFunction<JapiApi, Object[], Object>>() {
            private static final long serialVersionUID = 692058448926571189L;

            {
                put(cn.hutool.http.Method.POST, (t, u) -> JapiApiUtil.post(resolver, t, u));
                put(cn.hutool.http.Method.GET, (t, u) -> JapiApiUtil.get(resolver, t, u));
                put(cn.hutool.http.Method.PUT, (t, u) -> JapiApiUtil.put(resolver, t, u));
            }
        };

    }

    /**
     * 定义切入点，以带有{@link JapiApi}注解的方法作为切入点
     */
    @Pointcut("@annotation(top.v5it.japi.plus.sdk.JapiApi)")
    public void japiApiPointCut() {
    }

    @SuppressWarnings("unchecked")
    @Around(value = "japiApiPointCut()")
    public Object around(ProceedingJoinPoint point) {
        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        String threadName = Thread.currentThread().getName();
        String id = StrUtil.join("#", className, methodName, threadName);

        Object[] args = point.getArgs();
        Assert.notEmpty(args, () -> new IllegalArgumentException("请求参数不能为空"));

        // 记录业务操作用时
        StopWatch stopWatch = new StopWatch(id, false);
        stopWatch.start();

        Object result =
                getAnnotation(point)
                        .map(v -> mapper.get(v.method()).apply(v, args))
                        .orElse(null);

        stopWatch.stop();

        final long executeTime = stopWatch.getTotalTimeMillis();

        if (executeTime > TIMEOUT) {
            log.warn(stopWatch.prettyPrint());
        }

        return result;
    }

    /**
     * 是否存在注解，如果存在就获取
     *
     * @param joinPoint 切点
     */
    private Optional<JapiApi> getAnnotation(JoinPoint joinPoint) {
        return
                Optional.ofNullable(joinPoint)
                        .map(JoinPoint::getSignature)
                        .map(v -> (MethodSignature) v)
                        .map(MethodSignature::getMethod)
                        .filter(v -> v.isAnnotationPresent(JapiApi.class))
                        .map(v -> v.getAnnotation(JapiApi.class));
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.resolver = resolver;
    }
}
